from datetime import datetime
import logging
import random
from django.db.models import Q
from cart.utils import merge_cookie_cart_to_redis
from django_redis import get_redis_connection
from rest_framework import status,mixins
from rest_framework.decorators import action
from rest_framework.generics import GenericAPIView, CreateAPIView,RetrieveAPIView, UpdateAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.viewsets import GenericViewSet
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.views import JSONWebTokenAPIView, jwt_response_payload_handler, ObtainJSONWebToken
from goods.models import SKU
from goods.serializers import SKUSerializer
# from meiduo_mall.utils.captcha.captcha import captcha
from users import constants
from users.models import User
from rest_framework.response import Response
from rest_framework.views import APIView
from users.serializers import UserSerializer, UserDetailSerializer, EmailSerializer, AddressSerializer, \
    AddressTitleSerializer, BrowseHistorySerializer, UpdateUserSerializer, VerifySmscodeSerializer, \
    SavePasswordSerializer
from celery_tasks.sms.tasks import send_sms_code
from users.utils import generate_find_password_accesstoken, decode_accesstoken
from verifications.constans import SMS_CODE_REDIS_EXPIRES, SEND_SMS_CODE_INTERVAL

logger = logging.getLogger('django')

# 第零步-->打开找回页面获取图片验证码

# 第一步-->获取手机号和生成toekn
# GET /accounts/(?P<username>/w+)/sms/token/
class FindMobileView(APIView):

    def get(self, request, username):

        # 获取参数
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
        except User.DoesNotExist:
            return Response({"message": "输入用户名有误"}, status=status.HTTP_404_NOT_FOUND)

        mobile = user.mobile

        image_code = request.query_params.get('text')
        image_code_id = request.query_params.get('image_code_id')

        # 拼接redis存储图片验证码的key值
        image_code_real_key = "image_code:%s" % image_code_id

        # 查询redis获取图片验证码， 并进行验证
        redis_conn = get_redis_connection('default')
        image_code_real = redis_conn.get(image_code_real_key)
        if not image_code_real:
            return Response({"message": "图片验证码已过期"}, status=status.HTTP_400_BAD_REQUEST)

        if image_code_real.lower().decode() != image_code.lower():
            return Response({"message": "图片验证码输入有误"}, status=status.HTTP_400_BAD_REQUEST)

        redis_conn.delete(image_code_real_key)

        # 组织secret_mobile数据，返回前端
        secret_mobile = mobile[0: 3] + "****" + mobile[-5: -1]
        # 生成access_token，确保用户进行修改密码的一致性和有效期
        access_token = generate_find_password_accesstoken(mobile) # 确保用户进行修改密码的一致性和有效期

        return Response({
            "mobile": secret_mobile,
            "access_token": access_token
        }, status=status.HTTP_200_OK)

# 第二步-->验证第一步生成的access_token有效性,然后发送短信验证码
# GET /sms_codes/
class SMSCodeView(APIView):
    def get(self, request):
        # 获取参数access_token
        access_token = request.query_params.get('access_token', '')

        # 校验是否有access_token
        if not access_token:
            return Response({"message": "access_token缺失"}, status=status.HTTP_400_BAD_REQUEST)

        # 调用解密函数，对access_token进行解密
        mobile_dict = decode_accesstoken(access_token)

        # 解密失败
        if not mobile_dict:
            return Response({"message": "权限丢失"}, status=status.HTTP_400_BAD_REQUEST)

        mobile = mobile_dict['mobile']

        # 检验解密之后的mobile
        try:
            user = User.objects.get(mobile=mobile)
        except User.DoesNotExist:
            return Response({"message": "权限丢失"}, status=status.HTTP_401_UNAUTHORIZED)

        # 判断给'mobile'60s之内是否发过短信
        redis_conn = get_redis_connection('verify_codes')
        send_flag = redis_conn.get('send_flag_%s' % mobile)

        if send_flag:
            return Response({'message': '短信发送过于频繁'}, status=status.HTTP_403_FORBIDDEN)

        # 发出任务消息，使用celery异步任务队列，调worker发短信
        #  随机生成6位手机号
        sms_code = "%06d" % random.randint(0, 999999)
        logger.info('短信验证码的内容是：%s' % sms_code)

        # 创建redis-pipeline对象
        pl = redis_conn.pipeline()

        # 向redis管道中添加命令
        pl.setex('sms_%s' % mobile, SMS_CODE_REDIS_EXPIRES, sms_code) # 存入短信验证码
        pl.setex('send_flag_%s' % mobile, SEND_SMS_CODE_INTERVAL, sms_code) #

        # 一次性执行管道中的所有命令
        pl.execute()

        # 组织参数过期时间
        expires = SMS_CODE_REDIS_EXPIRES // 60

        # 发出任务消息
        send_sms_code.delay(mobile, sms_code, expires)

        # 返回响应
        return Response({"message": "OK"})

# 第三步-->验证短信验证码
# GET /accounts/(?P<username>\w+)/password/token/
class VerifySmscodeView(GenericAPIView):
    """短信验证码校验"""
    serializer_class = VerifySmscodeSerializer
    queryset = User.objects.all()

    def get(self, request, username):

        # 获取参数
        sms_code = request.query_params.get("sms_code")
        try:
            user = User.objects.get(Q(username=username)|Q(mobile=username))
        except User.DoesNotExist:
            return Response({"message": "用户名有误"})

        data = {
            'sms_code': sms_code,
            'user_id': user.id,
            'username': username
        }
        serializer = self.get_serializer(data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        return Response(serializer.data)


# 第四步-->保存要修改的密码 pk:username
# POST /users/(?P<pk>\d+)/password/
class SavePasswordView(GenericAPIView):
    serializer_class = SavePasswordSerializer
    queryset = User.objects.all()

    def post(self, request, pk):
        data = request.data

        # 给data添加pk字段，进行用户校验
        data['pk'] = pk
        # 获取用户对象
        user = self.get_object()

        serializer = self.get_serializer(user, data=data)
        serializer.is_valid(raise_exception=True)
        serializer.save()  # update
        return Response({"message": "OK"})


# 修改密码(修改成功跳转重新登录)
# PUT /user/(?P<user_id>\d+)/password/
class UpdateUserPWView(GenericAPIView):
    serializer_class = UpdateUserSerializer
    permission_classes = [IsAuthenticated]

    def put(self, request, user_id):
        # 1. 获取参数并进行校验(id是否正确, 密码是否正确, 两次密码是否一致)
        serializer = self.get_serializer(request.user, data=request.data)
        serializer.is_valid(raise_exception=True)
        # 2. 更新用户并保存到数据库create
        serializer.save()
        # 3. 修改成功, 返回响应
        return Response({"message": "OK"}, status=status.HTTP_201_CREATED)


#验证用户名重复接口
# GET /usernames/(?P<username>\w{5,20})/count/
# 参数url地址传参 username
# 返回值: {'username':*,'count'*}
class UserNameValid(APIView):
    def get(self, request, username):
        # 根据用户名查找数据库
        count = User.objects.filter(username=username).count()
        # 携带查到数量返回响应
        data = {
            'username': username,
            'count': count
        }
        return Response(data)

# 验证手机号重复接口
# GET /mobiles/(?P<mobile>1[3-9]\d{9})/count/
# 参数url地址传参 mobile
# 返回值: {'mobile':*,'count'*}from rest_framework
class MobileValid(APIView):
    def get(self, request, mobile):
        # 根据用户名查找数据库
        count = User.objects.filter(mobile=mobile).count()
        # 携带查到数量返回响应
        data = {
            'mobile': mobile,
            'count': count
        }
        return Response(data)

# 用户注册保存接口
# API: POST /users/
# 参数:{''username":**,"password":**,"password2":**,
#       "mobile":**, "sms_code":**, "allow":**}
# 响应: id,username,mobile
class UserSaveAPIView(CreateAPIView):
    serializer_class = UserSerializer

    # 继承CreateAPIVIew之后就不用写post方法了
    # def post(self,request):
    #     #１ 获取参数进行校验
    #     # 参数完整性, 是否同意协议, 手机号格式,手机号是否存在,两次密码一致性, 短信验证码是否正确
    #     serializer = self.get_serializer(data=request.data)
    #     #２　校验参数，校验错误直接跑出异常给客户端
    #     serializer.is_valid(raise_exception=True)
    #
    #     # ３ 创建新用户并保存到数据库中
    #     serializer.save()
    #
    #     # 4 注册成功,返回序列化结果serializer.data 201
    #     return Response(data=serializer.data,status=status.HTTP_201_CREATED)

# 用户登录接口 # JWT有个obtain_jwt_token方法定义好了登录视图,只需要根据需要自定义返回给前段的数据就好(默认只返回token)
# from rest_framework_jwt.views import obtain_jwt_token
# API: POST  /authorizations/
# 参数: username, password
# 响应: user_id,username,JWT token

# 重写obtain_jwt_token的登录类视图:为了调用合并购物车的函数
# POST  /authorizations/
class UserAuthorizeView(ObtainJSONWebToken):
    """用户登录视图"""
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            user = serializer.object.get('user') or request.user
            token = serializer.object.get('token')
            response_data = jwt_response_payload_handler(token, user, request)
            response = Response(response_data)
            if api_settings.JWT_AUTH_COOKIE:
                expiration = (datetime.utcnow() +
                              api_settings.JWT_EXPIRATION_DELTA)
                response.set_cookie(api_settings.JWT_AUTH_COOKIE,
                                    token,
                                    expires=expiration,
                                    httponly=True)
            merge_cookie_cart_to_redis(request,user,response)
            return response

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# 用户详情页面展示接口
# GET /user/
class UserDetailView(RetrieveAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = UserDetailSerializer

    def get_object(self):
        user = self.request.user
        return user
    # def get(self,request):
    #     # 1获取用户对象
    #     user = request.user
    #     # 2 将用户序列化返回
    #     serializer = self.get_serializer(user)
    #     return Response(serializer.data)

# 用户邮箱保存
# PUT /email/  参数:email  响应数据:id,email
class EmailView(UpdateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = EmailSerializer
    # 重写genericAPIView中的get_object方法,返回self.request.user用户对象
    def get_object(self):
        return self.request.user

    # def put(self,request):
    #     # 1 获取参数并校验 email比传,email的格式
    #     user = self.get_object()
    #     serializer = self.get_serializer(user,data = request.data)
    #     serializer.is_valid(raise_exception=True)
    #     # 2 设置登录用户的邮箱并给邮箱发邮件
    #     serializer.save()
    #     # 3 返回应答,邮箱设置成功
    #     return Response(serializer.data)

# PUT /emails/verification/?token=xxx
class VerifyEmailView(APIView):
    '''邮箱验证'''
    def put(self,request):
        # 获取token
        token = request.query_params.get('token')
        if not token:
            return Response({'message':'缺少token参数'},status=status.HTTP_400_BAD_REQUEST)

        # 验证token
        user = User.check_verify_email_token(token)
        if user is None:
            return Response({'message':'链接信息无效'},status=status.HTTP_400_BAD_REQUEST)
        else:
            user.email_active = True
            user.save()
            return Response({'message':'OK'})

# 用户地址管理-->操作同一组资源
class AddressViewSet(GenericViewSet):
    permission_classes = [IsAuthenticated]
    serializer_class = AddressSerializer
    # queryset=直接指定没有request对象,所以要重写get)queryset方法 返回用户收货地址的查询集
    def get_queryset(self):
        return self.request.user.addresses.filter(is_deleted=False)

    # 地址新增 POST /addresses/
    def create(self,request):
        '''
        request.user是获取登录用户的对象
        登录用户地址新增
        '''
        # 0 判断用户的地址数量是否超过数量上限,Address模型类里面related_name='addresses'
        count = request.user.addresses.filter().count()
        if count >= constants.USER_ADDRESS_COUNTS_LIMIT:
            return Response({'message':'地址数量超过限制'},status=status.HTTP_400_BAD_REQUEST)

        # 1 获取参数并进行校验(参数完整性,手机号格式,邮箱格式)
        serializer = self.get_serializer(data = request.data) # 这个方法内部会将request.user传到context参数,传给序列化器类对象self.context取
        serializer.is_valid(raise_exception=True)

        # 2 创建并保存新增地址数据
        # (create,重写序列化器自带的create,因为request.data里面由user对象,标识着新增的收获地址是哪个用户的)
        serializer.save()

        #　３　序列化地址返回
        return Response(data=serializer.data,status=status.HTTP_201_CREATED)

    # 获取登录用户地址数据 GET /addresses/
    def list(self,request):
        # 1 获取登录用户所有的地址数据
        addresses = self.get_queryset()
        # 2 将地址序列化并返回
        serializer = self.get_serializer(addresses,many=True)

        return Response({
            'user_id':request.user.id,
            'default_address_id':request.user.default_address_id,
            'limit':constants.USER_ADDRESS_COUNTS_LIMIT,
            'addresses':serializer.data
        })

    #　删除制定地址　DELETE /addresses/(?P<pk>\d+)
    def destroy(self,request,pk):
        # 1 根据ｐｋ获取制定的数据
        addr = self.get_object() # 默认根据ｐｋ获取指定对象数据

        # 2 将地址的is_deleted设置为True
        addr.is_deleted=True
        addr.save()
        # 3 返回应答
        return Response(status=status.HTTP_204_NO_CONTENT)

    # 修改指定地址 PUT /addresses/(?P<pk>\d+)/
    def update(self,request,pk):
        # 1 根据pk获取指定的地址数据
        addr = self.get_object()

        # 2 获取参数并进行校验
        serializer = self.get_serializer(addr,data=request.data)
        serializer.is_valid(raise_exception=True)

        # 3 修改指定地址数据(update)# 可以直接用ModelSerializer里面的update方法进行更新
        serializer.save()

        # 返回修改地址序列化数据
        return Response(serializer.data)

    # 设置登录用户的默认地址 PUT /address/(?P<pk>\d+)/status/
    @action(methods=['put'],detail=True)
    def status(self,request,pk):
        # 根据pk查询指定地址对象
        addr = self.get_object()

        # 设置登录用户的默认地址
        # 可以request.user.default_address_id=addr.id
        request.user.default_address = addr
        request.user.save()
        # 返回应答设置成功
        return Response({'message':'设置默认地址成功'})

    # 设置地址的标题# PUT /addresses/(?P<pk>\d+)/title/
    @action(methods=['put'],detail=True)
    def title(self,request,pk):
        # 根据pk查询指定地址
        addr = self.get_object()

        # 获取title进行反序列化参数并校验
        serializer = AddressTitleSerializer(addr,data=request.data)
        serializer.is_valid(raise_exception=True)
        # 修改指定地址的标题并更新数据库
        serializer.save()
        # 返回应答:标题设置成功
        return Response(serializer.data)


# 登录用户浏览记录新增和获取
class BrowseHistoryView(CreateAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = BrowseHistorySerializer
    # POST /browse_histories/
    # def post(self,request):
        # # 1 获取参数并校验（sku_id必传,sku_id商品是否存在,）
        # serializer = self.get_serializer(data=request.data)
        # serializer.is_valid(raise_exception=True)
        # # 2 再redis中存储登录用户的浏览记录
        # serializer.save()
        # # 3 返回应答
        # return Response(serializer.data,status=status.HTTP_201_CREATED)
    # 历史浏览记录获取
    def get(self,request):
        # 1 从redis中获取sku_id
        redis_conn = get_redis_connection('history')
        # 拼接redis_key
        history_key = 'history_%s'%request.user.id
        skus_id = redis_conn.lrange(history_key,0,-1)

        # 2 根据id去数据查询商品信息,保存在列表中
        sku_list = []
        for sku_id in skus_id :
            sku = SKU.objects.get(id=sku_id)
            sku_list.append(sku)

        # 3 将商品对象列表进行序列化返回
        serializer = SKUSerializer(sku_list,many=True) # 序列化多个对象要加many=True
        return Response(serializer.data)























